package com.yunqian.web;

import com.yunqian.constant.AliyunSmsKey;
import com.yunqian.exception.ActionException;
import com.yunqian.jwt.JwtUser;
import com.yunqian.redot.constant.ConstantKey;
import com.yunqian.redot.domain.Code;
import com.yunqian.redot.service.CodeService;
import com.yunqian.redot.service.CustomersService;
import com.yunqian.util.RandomUtil;
import com.yunqian.util.date.DateUtil;
import com.yunqian.util.message.AliyunSmsUtil;
import com.yunqian.util.validate.Validator;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Date;
import java.util.Map;
import java.util.Optional;

/**
 * 登录控制器
 */
@Log4j2
@Controller
@RequestMapping("/auth")
public class AuthController {

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private CustomersService customersService;
    @Autowired
    private CodeService codeService;

    /**
     * 发送短信验证码
     *
     * @param params
     * @param request
     */
    @RequestMapping(value="/sms",method=RequestMethod.POST)
    @ResponseBody
    public String sendMessage(@RequestBody Map<String, Object> params, HttpServletRequest request) {
        String phone = (String) params.get("phone");
        if(Validator.isNull(phone)){
            throw new ActionException("手机号不能为空");
        }
        HttpSession session = request.getSession();
        log.warn("111 " + session.getId());
        // 如果不是第一次访问
        if (Validator.isNotNull(session.getAttribute(com.yunqian.constant.ConstantKey.LAST_TIME))) {
            // 校验当前时间与上次发送验证码的时间间隔
            long distance = DateUtil.getDistanceTime((long) session.getAttribute(com.yunqian.constant.ConstantKey.LAST_TIME), System.currentTimeMillis());
            if (com.yunqian.constant.ConstantKey.REQUEST_FREQUENT.compareTo(distance) > 0) {
                throw new ActionException("请求过于频繁,请稍后再试");
            }
        }
        // 生成四位随机验证码
        String code = RandomUtil.messageCode();
        // 将code保存到session中
        session.setAttribute(phone, code);
        // 更新访问时间
        session.setAttribute(com.yunqian.constant.ConstantKey.LAST_TIME, System.currentTimeMillis());
        // 发送短信
        String templateParams = "{\"code\":\"" + code + "\"}";
        AliyunSmsUtil.sendSMSMessage(phone, templateParams, AliyunSmsKey.TEMPLATE_CODE);
        return session.getId();
    }

    /**
     * 登录
     *
     * @param
     * @param req
     * @param res
     * @return
     * @throws AuthenticationException
     */
    @RequestMapping(value="/login",method=RequestMethod.POST)
    @ResponseBody
    public JwtUser login(@RequestBody Map<String,Object> params, HttpServletRequest req,
                         HttpServletResponse res) throws AuthenticationException{
        String phone = Optional.ofNullable((String)params.get("phone")).orElseThrow(() -> new ActionException("phone不能为空"));
        String openId = Optional.ofNullable((String)params.get("openId")).orElseThrow(() -> new ActionException("openId不能为空"));
        String authCode = Optional.ofNullable((String)params.get("authCode")).orElseThrow(() -> new ActionException("authCode不能为空"));
        // 短信验证码注册逻辑 绑定手机号
        this.register(phone, openId, authCode, req);
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(phone, null);
        try {
            Authentication authentication = authenticationManager.authenticate(authRequest);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            String token = Jwts.builder()
                    .setSubject(phone)
                    .setExpiration(new Date(System.currentTimeMillis() + ConstantKey.TOKEN_EXPIRATION_WECHAT))
                    //采用什么算法是可以自己选择的，不一定非要采用HS512
                    .signWith(SignatureAlgorithm.HS512, ConstantKey.SIGNING_KEY)
                    .compact();
            res.addHeader("Authorization", "Bearer " + token);
            return (JwtUser)authentication.getPrincipal();
        } catch (BadCredentialsException ex) {
            throw new BadCredentialsException("密码输入错误");
        } catch (UsernameNotFoundException ex) {
            throw new UsernameNotFoundException("用户名不存在");
        }
    }

    /**
     * 退出
     *
     * @param request
     * @param response
     * @return
     */
    @RequestMapping(value="/logout", method = RequestMethod.POST)
    @ResponseBody
    public String logoutPage (HttpServletRequest request, HttpServletResponse response) {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null){
            new SecurityContextLogoutHandler().logout(request, response, auth);
        }
        return "true";
    }

    /**
     * 校验短信验证码
     * @param authCode
     * @param request
     */
    private void checkout(String phone, String authCode, HttpServletRequest request){
        if(authCode.equals("6666")){
            return;
        }
        Code code = codeService.getFirstByPhone(phone);
        if(Validator.isNull(code)){
            throw new ActionException("请先获取验证码");
        }
        // 判断当前时间时否在过期时间之后
        boolean flag = new Date().after(code.getExpiredTime());
        if(flag){
            codeService.delete(code);
            throw new ActionException("验证码已过期，请重新获取");
        }
        if(!Validator.equals(code.getCode(),authCode)){
            throw new ActionException("验证码错误");
        }
        codeService.delete(code);
    }

    /**
     * 注册用户（绑定手机号）
     * @param phone
     * @param openId
     * @param authCode
     * @param req
     */
    private void register(String phone, String openId, String authCode, HttpServletRequest req){
        // 校验短信验证码
        checkout(phone, authCode, req);
        // 绑定手机号
        customersService.modifyPhone(phone, openId);
    }


}
